package router

import (
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/storyicon/grbac"
	"net/http"
	"strings"
	"time"
	"web-service/admin-api/datasource"
	"web-service/admin-api/model"
)

//权限验证规则
type AuthorizationRule struct {
	Menu            model.Menu
	AuthorizedRoles []string
	ForbiddenRoles  []string
	AllowAnyone     bool
}

func LoadAuthorizationRules() (rules grbac.Rules, err error) {
	var menus []model.Menu
	db := datasource.NewOrm()

	//查询所有菜单
	db.Table(&menus).Where("status", "=", 1).Select()
	var authorizationRuleMap = make(map[int32]*AuthorizationRule)

	for _, v := range menus {
		me := (model.Menu)(v)
		//status=1,表示不限制访问
		authorizationRuleMap[me.Id] = &AuthorizationRule{Menu: me, AllowAnyone: true, AuthorizedRoles: []string{}, ForbiddenRoles: []string{}}
	}

	db = datasource.NewOrm()
	data, err := db.Table("rule").
		LeftJoin("role ro", "ro.id", "=", "rule.role_id").
		Fields("menu_id,GROUP_CONCAT(ro.name SEPARATOR ',') as role_names,status").
		GroupBy("menu_id,status").Get()
	if err != nil {
		panic(err)
	}

	for _, v := range data {
		menuId, _ := v["menu_id"].(int64)
		status := v["status"].(int64)
		role_names := v["role_names"]

		id := int32(menuId)
		st := int32(status)

		if _, ok := authorizationRuleMap[id]; ok {
			rnames, _ := role_names.(string)
			var roles []string
			roles = strings.Split(rnames, ",")
			if st == 1 {
				authorizationRuleMap[id].AuthorizedRoles = append(authorizationRuleMap[id].AuthorizedRoles, roles...)
			} else {
				authorizationRuleMap[id].ForbiddenRoles = append(authorizationRuleMap[id].ForbiddenRoles, roles...)
			}
		}
	}

	for _, v := range authorizationRuleMap {
		rbacRule := &grbac.Rule{Resource: &grbac.Resource{
			Host:   v.Menu.Host,
			Path:   v.Menu.Uri,
			Method: v.Menu.Method},
			Permission: &grbac.Permission{AuthorizedRoles: v.AuthorizedRoles, ForbiddenRoles: v.ForbiddenRoles, AllowAnyone: v.AllowAnyone}}
		rules = append(rules, rbacRule)
	}
	return rules, nil
}

//从请求中获取token，根据token查库得到角色名称
func QueryRolesByToken(request *http.Request) (roles []string, err error) {
	token := request.Form.Get("token")
	if len(token) <= 0 {
		token = request.URL.Query().Get("token")
	}

	db := datasource.NewOrm()
	data, err := db.Table("admin ad").LeftJoin("role ro", "ro.id", "=", "ad.role_id").Fields("name").Where("token", "=", token).Get()
	if err != nil {
		return roles, errors.New("token不正确")
	}

	if len(data) > 0 {
		role, _ := data[0]["name"].(string)
		roles = append(roles, role)
		return roles, nil
	} else {
		return roles, errors.New("token不正确")
	}
}

func Authorization() gin.HandlerFunc {
	rbac, err := grbac.New(grbac.WithLoader(LoadAuthorizationRules, time.Minute))
	if err != nil {
		panic(err)
	}

	return func(c *gin.Context) {
		roles, err := QueryRolesByToken(c.Request)
		if err != nil {
			c.AbortWithStatusJSON(200, gin.H{"status": 5000, "message": err.Error(), "data": ""})
			return
		}
		state, _ := rbac.IsRequestGranted(c.Request, roles)
		if !state.IsGranted() {
			c.AbortWithStatusJSON(200, gin.H{"status": 4000, "message": "没有权限访问此接口", "data": ""})
			return
		}
	}
}

/**
Wildcard supported syntax:
pattern:
  { term }
term:
  '*'         matches any sequence of non-path-separators
  '**'        matches any sequence of characters, including path separators.
  '?'         matches any single non-path-separator character
  '[' [ '^' ] { character-range } ']'
        character class (must be non-empty)
  '{' { term } [ ',' { term } ... ] '}'
  c           matches character c (c != '*', '?', '\\', '[')
  '\\' c      matches character c

character-range:
  c           matches character c (c != '\\', '-', ']')
  '\\' c      matches character c
  lo '-' hi   matches character c for lo <= c <= hi
*/
